﻿#region License and Terms
// MoreLINQ - Extensions to LINQ to Objects
// Copyright (c) 2008 Jonathan Skeet. All rights reserved.
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// These methods © 2013 Jon Hanna. Contributed to the above project.
// Licenses under the Apache License, Version 2.0 as per above.
#endregion
namespace MoreLinq
{
    using System;
    using System.Collections.Generic;
    public static partial class MoreEnumerable
    {
        /// <summary>
        /// Returns a count of the elements in a sequence, but does not count beyond a required number.
        /// </summary>
        /// <param name="source">The sequence to count the elements of.</param>
        /// <param name="required">The number to count up to.</param>
        /// <returns></returns>
        public static int CountUpTo<T>(this IEnumerable<T> source, int required)
        {
        	if(source == null) throw new ArgumentNullException("source");
        	if(required <= 0)
        	    return required;
        	ICollection<T> collection = source as ICollection<T>;
        	if(collection != null)//Calling .Count will almost certainly be faster; generally O(1) rather than O(n).
        	{
        		int count = collection.Count;
        		return count > required ? required : count;
        	}
        	int tally = 0;
    		using (IEnumerator<T> enumerator = source.GetEnumerator())
    			while(tally < required && enumerator.MoveNext())
    				tally++;
    		return tally;
        }
        /// <summary>
        /// Confirms a sequence has at least a certain number of elements.
        /// </summary>
        /// <param name="source">The sequence to count the elements of.</param>
        /// <param name="required">The number to test the sequence has at least this many elements.</param>
        /// <returns></returns>
        public static bool AtLeast<T>(this IEnumerable<T> source, int required)
        {
            return source.CountUpTo(required) == required;
        }
        /// <summary>
        /// Confirms a sequence has a certain number of elements.
        /// </summary>
        /// <param name="source">The sequence to count the elements of.</param>
        /// <param name="required">The number to test the sequence has this many elements.</param>
        public static bool Exactly<T>(this IEnumerable<T> source, int required)
        {
            if(required < 0 && source != null)//if null let the exception be triggered.
                return false;
            return source.CountUpTo(required + 1) == required;
        }
    }
}
